package com.hp.bon.sgw.snmp;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.text.ParseException;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.snmp4j.MessageDispatcher;
import org.snmp4j.MessageDispatcherImpl;
import org.snmp4j.TransportMapping;
import org.snmp4j.agent.AgentConfigManager;
import org.snmp4j.agent.DefaultMOContextScope;
import org.snmp4j.agent.DefaultMOQuery;
import org.snmp4j.agent.DefaultMOServer;
import org.snmp4j.agent.DuplicateRegistrationException;
import org.snmp4j.agent.MOQuery;
import org.snmp4j.agent.MOScope;
import org.snmp4j.agent.MOServer;
import org.snmp4j.agent.ManagedObject;
import org.snmp4j.agent.NotificationOriginator;
import org.snmp4j.agent.cfg.EngineBootsCounterFile;
import org.snmp4j.agent.io.DefaultMOPersistenceProvider;
import org.snmp4j.agent.io.MOInput;
import org.snmp4j.agent.io.MOInputFactory;
import org.snmp4j.agent.io.prop.PropertyMOInput;
import org.snmp4j.agent.mo.DefaultMOFactory;
import org.snmp4j.agent.mo.MOFactory;
import org.snmp4j.agent.mo.snmp.SnmpNotificationMIB;
import org.snmp4j.agent.mo.snmp.SnmpTargetMIB;
import org.snmp4j.agent.mo.snmp.TransportDomains;
import org.snmp4j.agent.mo.util.VariableProvider;
import org.snmp4j.agent.request.Request;
import org.snmp4j.agent.request.RequestStatus;
import org.snmp4j.agent.request.SubRequest;
import org.snmp4j.agent.request.SubRequestIterator;
import org.snmp4j.log.Log4jLogFactory;
import org.snmp4j.log.LogFactory;
import org.snmp4j.mp.MPv3;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.security.SecurityProtocols;
import org.snmp4j.smi.Address;
import org.snmp4j.smi.Counter32;
import org.snmp4j.smi.Gauge32;
import org.snmp4j.smi.GenericAddress;
import org.snmp4j.smi.Integer32;
import org.snmp4j.smi.IpAddress;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
import org.snmp4j.smi.UdpAddress;
import org.snmp4j.smi.Variable;
import org.snmp4j.smi.VariableBinding;
import org.snmp4j.transport.TransportMappings;
import org.snmp4j.util.ArgumentParser;
import org.snmp4j.util.ThreadPool;

public class SampleAgent
  implements VariableProvider
{
  private Logger logger = LoggerFactory.getLogger(SampleAgent.class);
  protected AgentConfigManager agent;
  protected MOServer server;
  private String configFile;
  private File bootCounterFile;
  private String startArg;
  private SGWMibSetter sgwMibSetter;
  protected final SgwMib sgwMib;
  private String trapAdress = "0.0.0.0/162";
  protected Properties tableSizeLimits;

  public SampleAgent()
  {
    this.sgwMib = new SgwMib(getFactory());
  }

  protected void addListenAddresses(MessageDispatcher md, List<String> addresses)
  {
    for (String addressString : addresses) {
      Address address = GenericAddress.parse(addressString);
      if (address == null) {
        this.logger.error("Could not parse address string '" + addressString + "'");
        return;
      }
      TransportMapping tm = TransportMappings.getInstance().createTransportMapping(address);
      if (tm != null)
        md.addTransportMapping(tm);
      else
        this.logger.warn("No transport mapping available for address '" + address + "'.");
    }
  }

  public void setStartArg(String startArg)
  {
    this.startArg = startArg;
  }

  public void run()
  {
    this.agent.initialize();

    registerMIBs();
    new Thread() {
      public void run() {
        SampleAgent.this.sgwMibSetter.setSgwMib(SampleAgent.this.sgwMib);
        SampleAgent.this.sgwMibSetter.setNotificationOriginator(SampleAgent.this.agent.getAgentNotificationOriginator());
        SampleAgent.this.sgwMibSetter.init();
      }
    }
    .start();

    this.agent.setupProxyForwarder();

    this.agent.setTableSizeLimits(this.tableSizeLimits);

    this.agent.run();
    addNotificationTargets(this.agent.getSnmpTargetMIB(), this.agent.getSnmpNotificationMIB());
    this.logger.info("start sgw snmp agent ");
  }

  public void shutdown() {
    this.logger.info("shutdown sgw agent");
    this.agent.saveState();
    this.agent.shutdown();
  }

  public void setSgwMibSetter(SGWMibSetter sgwSetter) {
    this.sgwMibSetter = sgwSetter;
  }

  protected MOFactory getFactory()
  {
    return DefaultMOFactory.getInstance();
  }

  protected void registerMIBs()
  {
    try
    {
      this.sgwMib.registerMOs(this.server, null);
    } catch (DuplicateRegistrationException drex) {
      this.logger.error("Duplicate registration: " + drex.getMessage() + "." + " MIB object registration may be incomplete!", drex);
    }
  }

  protected void addNotificationTargets(SnmpTargetMIB targetMIB, SnmpNotificationMIB notificationMIB)
  {
    targetMIB.addDefaultTDomains();
    targetMIB.addTargetAddress(new OctetString("notificationV2c"), TransportDomains.transportDomainUdpIpv4, new OctetString(new UdpAddress(this.trapAdress).getValue()), 200, 1, new OctetString("notify"), new OctetString("v2c"), 4);
    targetMIB.addTargetParams(new OctetString("v2c"), 1, 2, new OctetString("public"), 1, 4);
    notificationMIB.addNotifyEntry(new OctetString("default"), new OctetString("notify"), 1, 4);
    this.logger.info("SNMP trap address:" + this.trapAdress);
  }

  public Variable getVariable(String name)
  {
    this.logger.info("get name:" + name);

    OctetString context = null;
    int pos = name.indexOf(':');
    OID oid;
    if (pos >= 0) {
      context = new OctetString(name.substring(0, pos));
      oid = new OID(name.substring(pos + 1, name.length()));
    } else {
      oid = new OID(name);
    }
    final DefaultMOContextScope scope = new DefaultMOContextScope(context, oid, true, oid, true);
    MOQuery query = new DefaultMOQuery(scope, false, this);
    ManagedObject mo = this.server.lookup(query);
    if (mo != null) {
      final VariableBinding vb = new VariableBinding(oid);
      final RequestStatus status = new RequestStatus();
      SubRequest req = new SubRequest() {
        private boolean completed;
        private MOQuery query;

        public boolean hasError() { return false; }

        public void setErrorStatus(int errorStatus)
        {
          status.setErrorStatus(errorStatus);
        }

        public int getErrorStatus() {
          return status.getErrorStatus();
        }

        public RequestStatus getStatus() {
          return status;
        }

        public MOScope getScope() {
          return scope;
        }

        public VariableBinding getVariableBinding() {
          return vb;
        }

        public Request getRequest() {
          return null;
        }

        public Object getUndoValue() {
          return null;
        }

        public void setUndoValue(Object undoInformation) {
        }

        public void completed() {
          this.completed = true;
        }

        public boolean isComplete() {
          return this.completed;
        }

        public void setTargetMO(ManagedObject managedObject) {
        }

        public ManagedObject getTargetMO() {
          return null;
        }

        public int getIndex() {
          return 0;
        }

        public void setQuery(MOQuery query) {
          this.query = query;
        }

        public MOQuery getQuery() {
          return this.query;
        }

        public SubRequestIterator repetitions() {
          return null;
        }

        public void updateNextRepetition() {
        }

        public Object getUserObject() {
          return null;
        }

        public void setUserObject(Object userObject)
        {
        }
      };
      mo.get(req);
      this.logger.info("val:" + vb.getVariable());
      return vb.getVariable();
    }
    this.logger.info("can't found mo ");
    return null;
  }

  public void init()
  {
    LogFactory.setLogFactory(new Log4jLogFactory());
    ArgumentParser parser = new ArgumentParser("-c[s{=SampleAgent.cfg}] -bc[s{=SampleAgent.bc}] +ts[s] +cfg[s]", "#address[s<(udp|tcp|tls):.*[/[0-9]+]?>] ..");
    Map args = null;
    try {
      args = parser.parse(this.startArg.split(" "));

      SecurityProtocols.getInstance().addDefaultProtocols();
      this.configFile = ((String)((List)args.get("c")).get(0));
      this.bootCounterFile = new File((String)((List)args.get("bc")).get(0));

      this.server = new DefaultMOServer();
      MOServer[] moServers = { this.server };
      InputStream configInputStream = SampleAgent.class.getResourceAsStream("SampleAgentConfig.properties");
      if (args.containsKey("cfg")) {
        try {
          configInputStream = new FileInputStream((String)ArgumentParser.getValue(args, "cfg", 0));
        } catch (FileNotFoundException ex1) {
          ex1.printStackTrace();
        }
      }
      final Properties props = new Properties();
      try {
        props.load(configInputStream);
      } catch (IOException ex) {
        this.logger.error("load config err:" + ex);
      }
      MOInputFactory configurationFactory = new MOInputFactory() {
        public MOInput createMOInput() {
          return new PropertyMOInput(props, SampleAgent.this);
        }
      };
      InputStream tableSizeLimitsInputStream = SampleAgent.class.getResourceAsStream("SampleAgentTableSizeLimits.properties");
      if (args.containsKey("ts")) {
        try {
          tableSizeLimitsInputStream = new FileInputStream((String)ArgumentParser.getValue(args, "ts", 0));
        } catch (FileNotFoundException ex1) {
          ex1.printStackTrace();
        }
      }
      this.tableSizeLimits = new Properties();
      try {
        this.tableSizeLimits.load(tableSizeLimitsInputStream);
      } catch (IOException ex) {
        ex.printStackTrace();
      }
      MessageDispatcher messageDispatcher = new MessageDispatcherImpl();
      addListenAddresses(messageDispatcher, (List)args.get("address"));
      this.agent = new AgentConfigManager(new OctetString(MPv3.createLocalEngineID()), messageDispatcher, null, moServers, ThreadPool.create("SampleAgent", 3), configurationFactory, new DefaultMOPersistenceProvider(moServers, this.configFile), new EngineBootsCounterFile(this.bootCounterFile));
      run();
      for (int i = 1; i < 0; i++)
        this.agent.getAgentNotificationOriginator()
          .notify(new OctetString(), 
          SnmpConstants.coldStart, 
          new VariableBinding[] { new VariableBinding(new OID("1.3.6.1.4.0"), new Integer32(i)), new VariableBinding(new OID("1.3.6.1.4.0"), new Counter32(278070606L)), new VariableBinding(new OID("1.3.6.1.4.0"), new OctetString("Hello world!")), new VariableBinding(new OID("1.3.6.1.4.0"), new IpAddress("127.0.0.2")), 
          new VariableBinding(new OID("1.3.6.1.4.0"), new Gauge32(867685L)) });
    }
    catch (ParseException ex) {
      this.logger.error("init err " + ex.getMessage());
    }
  }

  public void setTrapAdress(String trapAdress) {
    this.trapAdress = trapAdress;
  }

  public static void main(String[] args)
  {
    LogFactory.setLogFactory(new Log4jLogFactory());
    ArgumentParser parser = new ArgumentParser("-c[s{=SampleAgent.cfg}] -bc[s{=SampleAgent.bc}] +ts[s] +cfg[s]", "#address[s<(udp|tcp|tls):.*[/[0-9]+]?>] ..");
    try {
      parser.parse(args);
    } catch (ParseException e) {
      e.printStackTrace();
    }

    StringBuilder sb = new StringBuilder();
    String[] arrayOfString = args; int j = args.length; for (int i = 0; i < j; i++) { String arg = arrayOfString[i];
      sb.append(arg).append(" ");
    }
    SampleAgent agent = new SampleAgent();
    agent.setStartArg(sb.toString());
    System.out.println("start ... ");
    agent.init();
    System.out.println("started ");
  }
}